home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / njsort11.arc / NJSORT.C < prev    next >
Text File  |  1989-01-02  |  33KB  |  1,213 lines

  1. /* TABs are every three columns    */
  2. /*---------------------------------------------------------
  3.  *                                                        *
  4.  *    Nifty James' Famous SORT Tool                                     *
  5.  * Version 1.00 of 08 May 1988                                     *
  6.  * Version 1.05 of 19 May 1988                                     *
  7.  * Version 1.10 of 03 July 1988                                     *
  8.  * Version 1.11 of 30 October 1988                                 *
  9.  *                                                                             *
  10.  * (C) Copyright 1988 by Mike Blaszczak                         *
  11.  * All Rights Reserved.  World Rights Reserved.                 *
  12.  *                                                                             *
  13.  * COMPILE WITH COMPACT MODEL!                                     *
  14.  * LINK with COMPARES.OBJ and /EXEPACK!                         *
  15.  *                                                                             *
  16.  * Written for Microsoft C Version 5.00 under MS-DOS 3.10 *
  17.  * Revised (slightly) for Microsoft C Version 5.10             *
  18.  *                                                                             *
  19.  --------------------------------------------------------*/
  20.  
  21. /* ------------------------------------------------------------------------ */
  22. /*    Program-specific #defines
  23. */
  24.  
  25. #define MAXFILES    17            /* maximum number of open temp files    */
  26. #define SWAPFILES    64            /* maximum number of merge files            */
  27. #define LINESIZE    2047        /* maximum characters on a line            */
  28. #define MEMLINES    (12*1024)/* maximum number of lines in memory    */
  29.  
  30. #define OUTOFMEM    1            /* errorlevel for out of memory            */
  31. #define BADUSAGE    2            /* errorlevel for bad command line        */
  32. #define BADFILE    3            /* errorlevel for bad file name            */
  33. #define BADREAD    4            /* something awry reading or writing    */
  34. #define BADWRITE    5
  35. #define BADPARAM    6            /* got an invalid/out of range param    */
  36.  
  37. /* ------------------------------------------------------------------------ */
  38. /* Debugging switches
  39. */
  40.  
  41. /* #define DEBUG                    /* general debugging switch                */
  42. /* #define MERGEBUG                /* debugging merge functions                */
  43.  
  44. /* ------------------------------------------------------------------------ */
  45. /*    Language-extension #defines
  46. */
  47.  
  48. #define ELEMENTS(array) (sizeof(array)/sizeof(array[0]))
  49.  
  50. #define boolean    unsigned char
  51. #define FALSE        0
  52. #define TRUE        (!FALSE)
  53.  
  54. /* ------------------------------------------------------------------------ */
  55. /* Needed #include files
  56. */
  57.  
  58. #include <dos.h>
  59. #include    <io.h>
  60. #include <malloc.h>
  61. #include <process.h>
  62. #include <stdio.h>
  63. #include <stdlib.h>
  64. #include <string.h>
  65. #include <time.h>
  66.  
  67. /* ------------------------------------------------------------------------ */
  68. /* File Buffers and File Name areas
  69. */
  70.  
  71. FILE *tempfiles[SWAPFILES];        /* array of pointers to temp file buffers */
  72. char *tfilenames[SWAPFILES];        /* names of those files                            */
  73. char *tempbuff[SWAPFILES];            /* line buffer for each file                    */
  74. boolean tempused[SWAPFILES];        /* set true if that file was used            */
  75. long tempoffset[SWAPFILES];        /* place we're reading from in the file    */
  76.  
  77. unsigned topfile = 0;                /* next unused temporary filename             */
  78.  
  79. FILE    *infile;                            /* the main inputfile                            */
  80. char    infilename[64];                /* the main inputfile's name                    */
  81.  
  82. FILE    *outfile;                        /* the file to be written out                    */
  83. char    outfilename[64];                /* the name of the file to be written out */
  84.  
  85. char    *lines[MEMLINES];                /* array of pointers to lines in memory    */
  86. int    topline;                            /* next unused line                                */
  87. char    buffer[LINESIZE];                /* buffer for reading and writing            */
  88.  
  89. char    infilemode[3]        = "r";    /* the modespec (ie, "r" or "rb") for        */
  90. char    outfilemode[3]        = "w";    /* the various files.  set up by                */
  91. char    *tempfileinmode = infilemode;                /* makefilemodes()                */
  92. char    *tempfileoutmode= outfilemode;
  93.  
  94. /* ------------------------------------------------------------------------ */
  95. /* global data
  96. */
  97.  
  98. int    readstatus;                        /* result of last call to readrecord()        */
  99. int    result;                            /* working result from fillmemory()            */
  100.  
  101. int    bigmark;                            /* used during the merge                        */
  102. /* lastbigmark was removed in Version 1.11    */
  103. /* int    lastbigmark;                /* previous largest from tempbuff[]            */
  104. char    *biggest;                        /* pointer to that entry in tempbuff[]        */
  105. unsigned workline;                    /* current dump point in lines[]                */
  106.  
  107. /* ------------------------------------------------------------------------ */
  108. /* Statistics and tallies
  109. */
  110.  
  111. unsigned    long exchanges = 0L;        /* number of exchanges made during sort    */
  112. unsigned    long nailed = 0L;            /* number of blanks we nailed                    */
  113. unsigned    long nuked = 0L;            /* number of dupes we nuked                    */
  114. unsigned    long nlines = 0L;            /* input lines                                        */
  115. clock_t    initiated, terminated;    /* starting and finishing clock() times    */
  116. clock_t    reading, sorting, writing, merging;
  117. clock_t    mergestart;                    /* time spent in each section of program    */
  118.  
  119. /* ------------------------------------------------------------------------ */
  120. /* Options and flags
  121. */
  122.  
  123. char         seper = '\0';                  /* delimiter between fields                    */
  124. boolean    reversed = FALSE;         /* sort in normal order by default            */
  125. boolean    ignorewhite = FALSE;        /* ignore leading whitespace                    */
  126. unsigned firstkey = 0;                  /* use a primary key                                */
  127. unsigned firstlen = LINESIZE;        /* width of primary key                            */
  128. unsigned secondkey = 0;                  /* use a secondary key                            */
  129. unsigned secondlen = LINESIZE;     /* width of second key                            */
  130. unsigned thirdkey = 0;                /* use a third key                                */
  131. unsigned thirdlen = LINESIZE;    /* width of third key                            */
  132. unsigned fourthkey = 0;                /* use a fourth key                                */
  133. unsigned fourthlen = LINESIZE;    /* width of fourth key                            */
  134. unsigned fifthkey = 0;                /* use a fifth key                                */
  135. unsigned fifthlen = LINESIZE;        /* width of fifth key                            */
  136. /* skiprecs is new for 1.11    */
  137. unsigned    skiprecs = 0;                /* # of records to leave in place 0=none    */
  138. boolean    blastdupes = FALSE;        /* erase duplicate lines                        */
  139. boolean    insensitive = FALSE;        /* ignore case                                        */
  140. boolean    skipblanks = FALSE;        /* remove blank lines from text                */
  141. boolean    verbose = FALSE;            /* verbose mode                                    */
  142. boolean    columns = FALSE;            /* use keys as colums and not fields        */
  143. size_t    recwidth = 0;                /* recwidth, 0 if not binary mode                */
  144.  
  145. boolean    gotout = FALSE;            /* set when outfile specified                    */
  146.  
  147. /* ------------------------------------------------------------------------ */
  148. /* Widely used messages
  149. */
  150.  
  151. char    *tempfileerror    = "\nCouldn't open file \"%s\" for temporary use.\n";
  152. char    *progname        = "NJSORT: ";
  153. char    *badwrite        = "NJSORT: Can't write record %u in file %s\n";
  154. char    *badfputs        = "NJSORT: Can't write line %u in file %s\n";
  155. char    *toobig            = " is wider than maximum record length";
  156. char    *canthave        = "% Can't have both %s\n";
  157.  
  158. /* ------------------------------------------------------------------------ */
  159. /* ANSI-Standard C-Language Declarations
  160. */
  161.  
  162. void        makefilenames(void);
  163. void        makefilemodes(void);
  164. void        writerecord(FILE *destfile, char *destname, char *record,
  165.                 unsigned number);
  166. void        drainmemory(FILE *destfile, char *destname);
  167. void        readrecord(char *destination, FILE *infile);
  168. void        skiprecords(void);
  169. unsigned    fillmemory(void);
  170. void        ssort(size_t elements, int (*cmpcall)(const char *, const char *));
  171. void        dosort(void);
  172. void        showusage(void);
  173. void        checkline(int, char**);
  174. void        statusout(void);
  175. void        pclock_t(clock_t t2print);
  176. void        outstats(void);
  177. void        main(int count, char *arglist[]);
  178.  
  179. /* ------------------------------------------------------------------------ */
  180. /* external (assembly-language) comapre routines, with ANSI-Standard
  181.  * function prototypes.
  182. */
  183.  
  184. extern int comp_0(const char *, const char *);
  185. extern int comp_0x(const char *, const char *);
  186. extern int comp_1(const char *, const char *);
  187. extern int comp_1x(const char *, const char *);
  188. extern int comp_2(const char *, const char *);
  189. extern int comp_3(const char *, const char *);
  190. extern int comp_bin(const char *, const char *);
  191. extern int comp_gen(const char *, const char *);
  192. extern int checkblank(char *string);
  193. extern unsigned long compcalls;
  194.  
  195. /* ----------------------------------------------------------------------- */
  196. /* This function makes the filenames for the program.  All the tfilenames
  197.     are made, based on the process name and the content of the TEMP=
  198.     environment variable.  The outfilename is made, based on the input
  199.     file name.
  200. */
  201.  
  202. void makefilenames()
  203. {
  204.     char name_buffer[64];    /* temporary buffer for building each name    */
  205.     char drive[3];                /* drive, dir, filename, and ext are used        */
  206.     char dir[30];                /* by _splitpath and _makepath to build the    */
  207.     char filename[9];            /* filenames.                                            */
  208.     char ext[4];
  209.  
  210.     int n, myid;                /* loop counter and place holder for our pid    */
  211.     char *restplace;            /* temporary pointer                                    */
  212.  
  213.     /* first, we'll chop up the input file name    */
  214.     _splitpath(infilename, drive, dir, filename, ext);
  215.  
  216.     /* if it has the extension .BAK, complain!    */
  217.     if (strcmp(ext, ".BAK") == 0)
  218.         {
  219.         fprintf(stderr, "%sCan't sort a file with extention .BAK\n", progname);
  220.         exit(BADFILE);
  221.         }
  222.  
  223.     /* if we never found a specific output file, create it here by
  224.         making .BAK the extension of the file name we were given        */
  225.     if (!gotout)
  226.         _makepath(outfilename, drive, dir, filename, ".BAK");
  227.  
  228.     /* try to find NJTEM, TMP, or TEMP in the environment    */
  229.  
  230.     restplace = getenv("NJTEMP");
  231.     if (restplace == NULL)
  232.         restplace = getenv("TMP");
  233.     if (restplace == NULL)
  234.         restplace = getenv("TEMP");
  235.     if (restplace != NULL)
  236.         _splitpath(restplace, drive, dir, filename, ext);
  237.  
  238.     /* now, find the process ID and use that to name our temp files    */
  239.  
  240.     myid = getpid();
  241.     sprintf(filename, "SRT%5.5d", myid);
  242.  
  243.     /* create a name for each temp file ... SRT<pid>.###    */
  244.  
  245.     for (n = 0; n<SWAPFILES; n++)
  246.         {
  247.         sprintf(ext, ".%3.3d", n);
  248.         _makepath(name_buffer, drive, dir, filename, ext);
  249.         restplace = strdup(name_buffer);
  250.         if (restplace == NULL)
  251.             {
  252.             fprintf(stderr, "%sNot enough memory\n", progname);
  253.             exit(OUTOFMEM);
  254.             }
  255.         tfilenames[n] = restplace;
  256.         }
  257.     return;
  258. }
  259.  
  260. /* ----------------------------------------------------------------------- */
  261. /* this function will set up the file mode variables so that they
  262.     correctly reflect the file mode that we will need to use.
  263. */
  264.  
  265. void    makefilemodes()
  266. {
  267.     /* the default modes are correct for ASCII sorting.    */
  268.  
  269.     if (!recwidth)
  270.         return;
  271.  
  272.     /* otherwise, we must concatinate a "b" for binary mode    */
  273.  
  274.     strcat(infilemode, "b");
  275.     strcat(outfilemode, "b");
  276.     return;
  277. }
  278.  
  279. /* ----------------------------------------------------------------------- */
  280. /*    This function is called by drainmemory() to write a record to the
  281.     output file.  It takes care of any errors that may occurr.
  282. */
  283.  
  284. void writerecord(FILE *destfile, char *destname, char *record,
  285.     unsigned number)
  286. {
  287.     register size_t written;
  288.  
  289.     /* if we're in binary mode, write() the record ... otherwise,
  290.         use fputs() to send it out to disk.                                    */
  291.  
  292.     if (recwidth)
  293.         {
  294.         /* in version 1.11, I changed this from an fwrite() call to
  295.             a write() call for greatly improved file creation speed.    */
  296.  
  297. /*        written = fwrite(record, sizeof(char), recwidth, destfile); */
  298.         written = write(fileno(destfile), record, recwidth);
  299.         if (written != recwidth)
  300.             {
  301.             fprintf(stderr, badwrite, number+1, destname);
  302.             exit(BADWRITE);
  303.             }
  304.         }
  305.     else
  306.         {
  307.         if (fputs(record, destfile))
  308.             {
  309.             fprintf(stderr, badfputs, number+1, destname);
  310.             exit(BADWRITE);
  311.             }
  312.         }
  313.  
  314.     return;
  315. }
  316.  
  317. /* ----------------------------------------------------------------------- */
  318. /* This function frees up all the memory pointed to by the lines[] array
  319.     as it writes the array to the specified file.
  320. */
  321.  
  322. void drainmemory(FILE *destfile, char *destname)
  323. {
  324.     register unsigned n;
  325.     register char *lastone;
  326.     clock_t writeentry;
  327.  
  328.     printf("Writing... ");
  329.     writeentry = clock();
  330.  
  331.     /* write one record, at least, and prime "writerecord"    */
  332.  
  333.     writerecord(destfile, destname, lines[0], 0);
  334.     lastone = lines[0];
  335.  
  336.     /* write each record from memory        */
  337.  
  338.     for (n = 1; n<topline; n++)
  339.         {
  340.  
  341.         /* if this one is the same as the last one, and blastdupes
  342.             is a selected option, don't write it but do remember that
  343.             we "nuked" it ... for the statistics display in /v            */
  344.  
  345.         if (blastdupes && (strcmp(lines[n], lastone) == 0))
  346.             nuked++;
  347.         else
  348.             writerecord(destfile, destname, lines[n], n);
  349.  
  350.         /* free the memory from the record previous    */
  351.  
  352.         free(lines[n-1]);
  353.         lines[n-1] = NULL;
  354.         lastone = lines[n];
  355.         }
  356.  
  357.     /* free the last record, and now add the time we spent here to the
  358.         total    */
  359.  
  360.     free(lines[n]);
  361.     writing += clock()-writeentry;
  362.  
  363.     return;
  364. }
  365.  
  366. /* ----------------------------------------------------------------------- */
  367. /* fillmemory() depends on this routine to read a line or block into    buffer.
  368.     readrecord() is used by fillmemory() when the program needs a line or
  369.     record from the input file.  it reads the block into buffer and reports
  370.     any errors that occur.
  371. */
  372.  
  373. void readrecord(char *destspot, FILE *sourcefile)
  374. {
  375.     register size_t wegot;
  376.  
  377.     /* do this for binary modes    */
  378.     if (recwidth)
  379.         {
  380.         wegot = fread((char *) destspot, sizeof(char), recwidth, sourcefile);
  381.         if (wegot == 0 || wegot == recwidth)
  382.             return;
  383.         fprintf(stderr, "\n%sRecord %lu was incompletely read,", progname, nlines);
  384.         fprintf(stderr, " only %u of %u bytes.\n", wegot, recwidth);
  385.  
  386.         exit(BADREAD);
  387.         }
  388.     else
  389.     /* do this for ASCII mode    */
  390.         {
  391.         if (feof(sourcefile))
  392.             return;
  393.         fgets(destspot, ELEMENTS(buffer), sourcefile);
  394.         if (destspot[strlen(destspot)-1] != '\n')
  395.             {
  396.             fprintf(stderr, "%sInput line %lu exceeds %u characters.\n", progname, nlines, LINESIZE);
  397.             exit(BADREAD);
  398.             }
  399.         }
  400.  
  401.     return;
  402. }
  403.  
  404. /* ----------------------------------------------------------------------- */
  405. /* This function checks to see if the user asked that we skip any records.
  406.     If it is so, we will skip the records by reading them in from the
  407.     input file and then and writing them out to the output file before the
  408.     sort even starts (we are called from main()).
  409. */
  410.  
  411. void    skiprecords()
  412. {
  413.     unsigned    temporary;
  414.  
  415.     if (!skiprecs)
  416.         return;
  417.  
  418.     /* there are records to skip    */
  419.  
  420.     printf("Skipping %u record", skiprecs);
  421.     if (skiprecs > 1)
  422.         putchar('s');
  423.     putchar('\n');
  424.  
  425.     /* just read through and skip them!  */
  426.  
  427.     for (temporary = skiprecs; temporary > 0; temporary--)
  428.         {
  429.         if (feof(infile))
  430.             {
  431.             fprintf(stderr, "%sthere weren't enough input records to skip %u\n",    progname, skiprecs);
  432.             exit(BADPARAM);
  433.             }
  434.  
  435.         readrecord(buffer, infile);
  436.         writerecord(outfile, outfilename, buffer, skiprecs-temporary+1);
  437.         }
  438.  
  439.     return;
  440. }
  441.  
  442. /* ----------------------------------------------------------------------- */
  443. /* This function reads as much as possible from the input file.  If all
  444.     the file was read into memory (if EOF was reached), the routine returns
  445.     zero.  Otherwise, it returns a 1 when it runs out of memory or runs out
  446.     of slots in the lines[] array.
  447. */
  448.  
  449. unsigned fillmemory()
  450. {
  451.     register char *place;            /* destination for the item we just read    */
  452.     clock_t readentry;                /* time it took us to read things            */
  453.     boolean    extraused;                /* TRUE if we used the extra memory            */
  454.     char *extra;                        /* a pointer to the extra memory record    */
  455.  
  456.     /* tell the world what we're up to    */
  457.  
  458.     printf("\nReading... ");
  459.     topline = 0;
  460.     readentry = clock();
  461.  
  462.     /* initialize the extra memory area    */
  463.     extraused = FALSE;
  464.     if (!recwidth)
  465.         extra = (char *) malloc(LINESIZE);
  466.     else
  467.         extra = (char *) malloc(recwidth);
  468.  
  469.     /* read one record    */
  470.  
  471.     readrecord(buffer, infile);
  472.  
  473.     /* while the file isn't over    */
  474.  
  475.     while(!feof(infile))
  476.         {
  477.         /* increment the number of record read    */
  478.         nlines++;
  479.  
  480.         /* if we're doing line by line, malloc the length of the string    */
  481.         if (!recwidth)
  482.             {
  483.             if (checkblank(buffer))
  484.                 {
  485.                 nailed++;
  486.                 continue;
  487.                 }
  488.             place = malloc(strlen(buffer)+1);
  489.             }
  490.         /* otherwise, malloc the width of a record    */
  491.         else
  492.             place = malloc(recwidth);
  493.  
  494.         /* if we ran out of memory just then, use the extra bit of memory
  495.             that    we grabbed on the way in here.                                    */
  496.  
  497.         if (place == NULL)
  498.             {
  499.             lines[topline++] = extra;
  500.             if (!recwidth)
  501.                 strcpy(extra, buffer);
  502.             else
  503.                 memcpy(extra, buffer, recwidth);
  504.             reading += clock()-readentry;
  505.             return(1);
  506.             }
  507.  
  508.         /* otherwise, just add the place to the top of our list    */
  509.         else
  510.             lines[topline++] = place;
  511.  
  512.         /* and copy the information in to there        */
  513.         if (recwidth)
  514.             memcpy(place, buffer, recwidth);
  515.         else
  516.             strcpy(place, buffer);
  517.  
  518.         /* if we're at the end of our rope, give up    */
  519.         if (topline == MEMLINES)
  520.             {
  521.             reading += clock()-readentry;
  522.             return(1);
  523.             }
  524.  
  525.         /* otherwise, have another    */
  526.         readrecord(buffer, infile);
  527.         }
  528.  
  529.  
  530.     /* if the file ended and we read everything, we didn't need the
  531.         extra  bit of memory ... so we can free it up.    */
  532.     free(extra);
  533.  
  534.     reading += clock()-readentry;
  535.  
  536.     return(0);
  537. }
  538.  
  539. /* ----------------------------------------------------------------------- */
  540. /* This is the sort engine.
  541. */
  542.  
  543. void    ssort(size_t elements, int (*cmpcall)(const char *, const char *))
  544. {
  545.     register int    i, j;
  546.     int                gap;
  547.     char                *p1, *p2, *temp;
  548.  
  549.     for (gap=1; gap <= elements; gap = 3*gap + 1)
  550.         ;
  551.  
  552.     for (gap /= 3;  gap > 0; gap /= 3)
  553.         {
  554.         for (i = gap; i < elements; i++)
  555.             for (j=i-gap; j >= 0; j -= gap)
  556.             {
  557.                 p1 = lines[j];
  558.                 p2 = lines[j+gap];
  559.  
  560.                 if ((*cmpcall)(p1, p2) <= 0)
  561.                     break;
  562.  
  563.                 exchanges++;
  564.                 temp = lines[j];
  565.                 lines[j] = lines[j+gap];
  566.                 lines[j+gap] = temp;
  567.                 
  568.             }
  569.         putchar('.');
  570.         }
  571. }
  572.  
  573. /* ----------------------------------------------------------------------- */
  574. /*    This routine sorts all the information in memory.  It calls ssort() for
  575.     most of the work.
  576. */
  577.  
  578. void dosort()
  579. {
  580.     clock_t sortentry;
  581.  
  582.     if (topline == 0)
  583.         return;
  584.  
  585.     sortentry = clock();
  586.     printf("Sorting...");
  587.  
  588.     /* decide what compare routine to call ssort() with, and just do it    */
  589.  
  590.    if (recwidth)
  591.       ssort((size_t) topline, comp_bin);
  592.     else
  593.         {
  594.        if (columns || seper)
  595.           ssort((size_t) topline, comp_3);
  596.         else
  597.             {
  598.             if (ignorewhite)
  599.                 ssort((size_t) topline, comp_2);
  600.             else
  601.                 ssort((size_t) topline, comp_1x);
  602.             }
  603.         }
  604.  
  605.     putchar(' ');
  606.  
  607.     sorting += clock()-sortentry;
  608.     return;    
  609. }
  610.  
  611. /* ----------------------------------------------------------------------- */
  612. /* showusage() prints out the usage of the program to educate the user
  613. */
  614.  
  615. void showusage()
  616. {
  617.     fprintf(stderr, "Usage:\t%s <infile> [<outfile>] [<options>]\n", progname);
  618.     fputs("\tif <outfile> is unspecified, <infile> will be rewritten as .BAK\n\n", stderr);
  619.     fputs("\t<options> are:\n", stderr);
  620.     fputs("\t\t/F# specify first key field number\n", stderr);
  621.     fputs("\t\t/S# specify second key field number\n", stderr);
  622.     fputs("\t\t/T# specify third key field number\n", stderr);
  623.     fputs("\t\t/Q# specify fourth key field number\n", stderr);
  624.     fputs("\t\t/P# specify fifth key field number\n", stderr);
  625.     fputs("\t\t/Mc specify field separation character\n", stderr);
  626.     fputs("\t\t/N  reguard key numbers as column numbers\n\n", stderr);
  627.  
  628.     fputs("\t\t/B  remove blank lines\n", stderr);
  629.     fputs("\t\t/C  case insensitive sort\n", stderr);
  630.     fputs("\t\t/D  remove duplicate lines\n", stderr);
  631.     fputs("\t\t/I  ignore leading whitespace\n", stderr);
  632.     fputs("\t\t/On leave first n records of file in order\n", stderr);
  633.     fputs("\t\t/R  sort in reverse order\n", stderr);
  634.     fputs("\t\t/V  display sort stats\n", stderr);
  635.     fputs("\t\t/W# specify record width and use binary mode", stderr);
  636.     exit(BADUSAGE);
  637. }
  638.  
  639. /* ----------------------------------------------------------------------- */
  640. /* This routine sets the flags and checks the arguments.
  641.     It also sets up the firstlen ... fifthlen variables.
  642. */
  643.  
  644. void checkline(int aargc, char *aargv[])
  645. {
  646.     boolean gotin = 0;
  647.     char *temp;
  648.     int stepper;
  649.     int widest;
  650.  
  651.     for (stepper = 1; stepper<aargc; stepper++)
  652.         {
  653.         strupr(aargv[stepper]);
  654.         if (*aargv[stepper] != '/' && *aargv[stepper] != '-')
  655.             {
  656.             if (gotin && gotout)
  657.                 showusage();
  658.             if (gotin)
  659.                 {
  660.                 strcpy(outfilename, aargv[stepper]);
  661.                 gotout = TRUE;
  662.                 }
  663.             else
  664.                 {
  665.                 strcpy(infilename, aargv[stepper]);
  666.                 gotin = TRUE;
  667.                 }
  668.             }
  669.         else
  670.             {
  671.             switch (aargv[stepper][1])
  672.                 {
  673.                 case 'B':    skipblanks = TRUE;
  674.                                 break;
  675.                 case 'C':    insensitive = TRUE;
  676.                                 break;
  677.                 case 'D':    blastdupes = TRUE;
  678.                                 break;
  679.                 case 'F':    firstkey = (unsigned) atol(&aargv[stepper][2]);
  680.                                 break;
  681.                 case 'I':    ignorewhite = TRUE;
  682.                                 break;
  683.                 case 'M':    seper = aargv[stepper][2];
  684.                                 break;
  685.                 case 'N':    columns = TRUE;
  686.                                 break;
  687.                 case 'O':    skiprecs = (unsigned) atol(&aargv[stepper][2]);
  688.                                 break;
  689.                 case 'P':   fifthkey = (unsigned) atol(&aargv[stepper][2]);
  690.                                 break;
  691.                 case 'Q':    fourthkey = (unsigned) atol(&aargv[stepper][2]);
  692.                                 break;
  693.                 case 'R':    reversed = TRUE;
  694.                                 break;
  695.                 case 'S':    secondkey = (unsigned) atol(&aargv[stepper][2]);
  696.                                 break;
  697.                 case 'T':    thirdkey = (unsigned) atol(&aargv[stepper][2]);
  698.                                 break;
  699.                 case 'V':   verbose = TRUE;
  700.                                 break;
  701.                 case 'W':   recwidth = (unsigned) atol(&aargv[stepper][2]);
  702.                                 break;
  703.                 default:        showusage();
  704.                 }
  705.             }
  706.         }
  707.  
  708.     /* now that we have all of the options, check their validity    */
  709.  
  710.     if (columns && seper)
  711.         {
  712.         fprintf(stderr, canthave, progname, "/N and /M");
  713.         exit(BADUSAGE);
  714.         }
  715.  
  716.     if (seper && recwidth)
  717.         {
  718.         fprintf(stderr, canthave, progname, "/M and /W");
  719.         exit(BADUSAGE);
  720.         }
  721.  
  722.     if (recwidth && columns)
  723.         {
  724.         fprintf(stderr, canthave, progname, "/W and /N");
  725.         exit(BADUSAGE);
  726.         }
  727.  
  728.     if (recwidth && blastdupes)
  729.         {
  730.         fprintf(stderr, canthave, progname, "/W and /D");
  731.         exit(BADUSAGE);
  732.         }
  733.  
  734.     if (recwidth && ignorewhite)
  735.         {
  736.         fprintf(stderr, canthave, progname, "/W and /I");
  737.         exit(BADUSAGE);
  738.         }
  739.  
  740.     if (recwidth && insensitive)
  741.         {                                             
  742.         fprintf(stderr, canthave, progname, "/W and /C");
  743.         exit(BADUSAGE);
  744.         }
  745.  
  746.    if (recwidth)
  747.       if (firstkey>recwidth || secondkey>recwidth || thirdkey>recwidth ||
  748.             fourthkey>recwidth || fifthkey>recwidth)
  749.          {
  750.          fprintf(stderr, "%skey offset is greater than recwidth.\n", progname);
  751.          exit(BADUSAGE);
  752.          }
  753.  
  754.     if (!firstkey && !secondkey && !thirdkey && !fourthkey && !fifthkey &&
  755.             (columns || seper))
  756.         {
  757.         fprintf(stderr, "%smust have at least one field with /N or /M\n", progname);
  758.         exit(BADUSAGE);
  759.         }
  760.  
  761.     if (!gotin)
  762.         showusage();
  763.  
  764.     if (!columns && !recwidth)
  765.         return;
  766.  
  767.     if (firstkey > LINESIZE)
  768.         {
  769.         fprintf(stderr, "%sfirstkey%s\n", progname, toobig);
  770.         exit(BADPARAM);
  771.         }
  772.  
  773.     if (secondkey > LINESIZE)
  774.         {
  775.         fprintf(stderr, "%ssecondkey%s\n", progname, toobig);
  776.         exit(BADPARAM);
  777.         }
  778.  
  779.     if (thirdkey > LINESIZE)
  780.         {
  781.         fprintf(stderr, "%sthirdkey%s\n", progname, toobig);
  782.         exit(BADPARAM);
  783.         }
  784.  
  785.     if (fourthkey > LINESIZE)
  786.         {
  787.         fprintf(stderr, "%sthirdkey%s\n", progname, toobig);
  788.         exit(BADPARAM);
  789.         }
  790.  
  791.     if (fifthkey > LINESIZE)
  792.         {
  793.         fprintf(stderr, "%sthirdkey%s\n", progname, toobig);
  794.         exit(BADPARAM);
  795.         }
  796.  
  797.  
  798.     /*  this code computes the width of each field:
  799.          if a field is bounded on the right by another field, then its width
  800.          is computed.  if a field isn't bounded on the right by another field,
  801.          its width is set to the maximum ... LINESIZE.                */
  802.  
  803.     for (stepper = 0; stepper<LINESIZE; stepper++)
  804.             buffer[stepper] = ' ';
  805.     buffer[LINESIZE-1] = '\0';
  806.  
  807.     if (recwidth)
  808.         widest = recwidth;
  809.     else
  810.         widest = LINESIZE;
  811.  
  812.     buffer[firstkey]    = '*';
  813.     buffer[secondkey]    = '*';
  814.     buffer[thirdkey]    = '*';
  815.     buffer[fourthkey] = '*';
  816.     buffer[fifthkey]    = '*';
  817.     if (recwidth)
  818.         buffer[recwidth] = '*';            /* make sure that we don't overstep the
  819.                                                     buffer on the binary sort    */
  820.  
  821.     temp = strchr(buffer+firstkey+1, '*');
  822.     if (temp == NULL)
  823.         firstlen = widest;
  824.     else
  825.         firstlen = temp - (buffer+firstkey);
  826.  
  827.     temp = strchr(buffer+secondkey+1, '*');
  828.     if (temp == NULL)
  829.         secondlen = widest;
  830.     else
  831.         secondlen = temp - (buffer+secondkey);
  832.  
  833.     temp = strchr(buffer+thirdkey+1, '*');
  834.     if (temp == NULL)
  835.         thirdlen = widest;
  836.     else
  837.         thirdlen = temp - (buffer+thirdkey);
  838.  
  839.     temp = strchr(buffer+fourthkey+1, '*');
  840.     if (temp == NULL)
  841.         fourthlen = widest;
  842.     else
  843.         fourthlen = temp - (buffer+fourthkey);
  844.  
  845.     temp = strchr(buffer+fifthkey+1, '*');
  846.     if (temp == NULL)
  847.         fifthlen = widest;
  848.     else
  849.         fifthlen = temp - (buffer+fifthkey);
  850.  
  851.     return;
  852. }
  853.  
  854. /* ----------------------------------------------------------------------- */
  855. /* This routine lets the user know what's up.
  856. */
  857.  
  858. void statusout()
  859. {
  860.     printf("Sorting file \"%s\" into \"%s\"\n", infilename, outfilename);
  861.     if (blastdupes)
  862.         puts("Removing duplicate lines");
  863.     if (skipblanks)
  864.         puts("Removing blank lines");
  865.  
  866.     if (insensitive)
  867.         puts("Case insensitive sort");
  868.     if (reversed)
  869.         puts("Backwards sort order");
  870.  
  871.     if (ignorewhite)
  872.         puts("Ignore leading whitespace");
  873.     if (verbose)
  874.         puts("Verbose mode");
  875.  
  876.     if (columns || seper)
  877.         {
  878.         printf("Sorting by %s\n", columns ? "columns" : "keys");
  879.         if (firstkey)
  880.             printf(" Key 1 = %4u   ", firstkey);
  881.         if (secondkey)
  882.             printf(" Key 2 = %4u   ", secondkey);
  883.         if (thirdkey)
  884.             printf(" Key 3 = %4u   ", thirdkey);
  885.         if (fourthkey)
  886.             printf(" Key 4 = %4u   ", fourthkey);
  887.         if (fifthkey)
  888.             printf(" Key 5 = %4u", fifthkey);
  889.         }
  890.     if (columns)
  891.         {
  892.         if (firstkey)
  893.             printf("\nWidth 1= %4u   ", firstlen);
  894.         if (secondkey)
  895.             printf("Width 2= %4u   ", secondlen);
  896.         if (thirdkey)
  897.             printf("Width 3= %4u   ", thirdlen);
  898.         if (fourthkey)
  899.             printf("Width 4= %4u   ", fourthlen);
  900.         if (fifthkey)
  901.             printf("Width 5= %4u\n", fifthlen);
  902.         }
  903.  
  904.     if (recwidth)
  905.         {
  906.         printf("Binary mode - record width is %d byte", recwidth);
  907. /* version 1.11        */
  908.         if (recwidth > 1)
  909.             putchar('s');
  910.         putchar('\n');
  911. /* version 1.11 ends    */
  912.         }
  913.  
  914.     putchar('\n');
  915.     return;
  916. }
  917.  
  918. /* ----------------------------------------------------------------------- */
  919. /* This routine prints out a clock_t variable as a decimal number.  NOTE
  920.     that this routine relies upon CLK_TCK being #defined to 1000.  Other
  921.     compilers (than Microsoft) might not use this!   Turbo C does not!
  922. */
  923.  
  924. void pclock_t(clock_t t2print)
  925. {
  926.     printf("%7ld.%02ld", t2print/CLK_TCK, t2print%(CLK_TCK*10));
  927.     return;
  928. }
  929.  
  930. /* ----------------------------------------------------------------------- */
  931. /* This function prints the verbose mode statistics to the user.
  932. */
  933.  
  934. void outstats()
  935. {
  936.     register char *itemword;
  937.  
  938.     if (!verbose)
  939.         return;
  940.  
  941.     terminated = clock();
  942.  
  943.     if (recwidth)
  944.         itemword = "records";
  945.     else
  946.         itemword = " lines ";
  947.  
  948.     printf("\nTemporary files used     : %10d\n", topfile);
  949.     printf("Total %s read       : %10lu  Time Read   : ", itemword, nlines);
  950.     pclock_t(reading);
  951.     printf("\nBlank %s removed    : %10lu  Time Writing: ", itemword, nailed);
  952.     pclock_t(writing);
  953.     printf("\nDuplicate %s removed: %10lu  Time Sorting: ", itemword, nuked);
  954.     pclock_t(sorting);
  955.     printf("\nTotal %s written    : %10lu  Time Merging: ", itemword,
  956.                                                     nlines - nailed - nuked);
  957.     pclock_t(merging);
  958.  
  959.     
  960.     printf("\n\nCompare routine calls    : %10lu  ", compcalls);
  961.     printf("Elapsed time      :  ");
  962.     pclock_t(terminated-initiated);
  963.     printf("\nExchanges during sort    : %10lu  ", exchanges);
  964.  
  965.     printf("%s per second: %10ld\n\n", itemword,
  966.                                     (nlines*CLK_TCK)/(terminated-initiated));
  967.     return;
  968. }
  969.  
  970. /* ----------------------------------------------------------------------- */
  971.  
  972. void main(int argc, char *argv[])
  973. {
  974.     register int gc;        /* general purpose loop counter    */
  975.  
  976.     for (gc = 0; gc<SWAPFILES; gc++)
  977.         {
  978.         tempused[gc] = FALSE;
  979.         tempoffset[gc] = 0L;
  980.         }
  981.  
  982.     reading = writing = sorting = merging = 0L;
  983.  
  984.     fclose(stdin);
  985.     fclose(stdprn);
  986.     fclose(stdaux);        /* free up more file handles    */
  987.  
  988.     puts("Nifty James' Famous SORT Tool\nVersion 1.11 of 30 October 1988");
  989.     puts("(C) Copyright 1988 by Mike Blaszczak\n");
  990.  
  991.     if (argc<2)
  992.         showusage();
  993.  
  994.     checkline(argc, argv);
  995.     makefilenames();
  996.  
  997.     makefilemodes();
  998.  
  999.     initiated = clock();
  1000.     infile = fopen(infilename, infilemode);
  1001.     if (infile == NULL)
  1002.         {
  1003.         fprintf(stderr, "%sCan't open file \"%s\" for input.\n", progname, infilename);
  1004.         exit(BADREAD);
  1005.         }
  1006.  
  1007.     outfile = fopen(outfilename, outfilemode);
  1008.     if (outfile == NULL)
  1009.         {
  1010.         fprintf(stderr, "%sCouldn't open file \"%s\" for output.\n",
  1011.             progname, outfilename);
  1012.         exit(BADWRITE);
  1013.         }
  1014.  
  1015.     /* the nitty-gritty    */
  1016.     statusout();
  1017.  
  1018. /* version 1.11    */
  1019.     /* at this point, the input file is open.  check to see if we're
  1020.         supposed to skip any of it off the top.            */
  1021.  
  1022.     skiprecords();
  1023.  
  1024. /* end version 1.11    */
  1025.  
  1026.     result = fillmemory();    /* read some of the file    */
  1027.  
  1028.     dosort();
  1029.     if (result == 0)            /* enirely in-memory sort, if it fits    */
  1030.         {
  1031.         drainmemory(outfile, outfilename);
  1032.         fcloseall();
  1033.         puts("Sorted!\007");
  1034.         outstats();
  1035.         exit(0);
  1036.         }
  1037.  
  1038.     /*    can't sort in one pass ... so, we'll use the tempfiles    */
  1039.  
  1040.     do {
  1041.         tempfiles[topfile] = fopen(tfilenames[topfile], tempfileoutmode);
  1042.         if (tempfiles[topfile] == NULL)
  1043.             {
  1044.             fprintf(stderr, tempfileerror, tfilenames[topfile]);
  1045.             exit(BADWRITE);
  1046.             }
  1047.         drainmemory(tempfiles[topfile], tfilenames[topfile]);
  1048.  
  1049.         if (recwidth)
  1050.             tempbuff[topfile] = (char *) malloc(recwidth);
  1051.         else
  1052.             tempbuff[topfile] = (char *) malloc(LINESIZE);
  1053.  
  1054.         if (tempbuff[topfile] == NULL)
  1055.             {
  1056.             fprintf(stderr, "%sCouldn't setup temporary buffer #%d\n",
  1057.                 progname, topfile);
  1058.             exit(BADWRITE);
  1059.             }
  1060.         fclose(tempfiles[topfile]);
  1061.         topfile++;
  1062.         if (topfile == MAXFILES)
  1063.             {
  1064.             fprintf(stderr, "%sToo many temporary files!  Can't sort.\n",
  1065.                 progname, topfile);
  1066.             exit(BADWRITE);
  1067.             }
  1068.         result = fillmemory();
  1069.         dosort();
  1070.     } while (result != 0);
  1071.     fclose(infile);
  1072.  
  1073.     /* Now, there is some sorted data in memory and in each of the temfiles
  1074.         we just merge these together into the outfile.                                */
  1075.  
  1076.     /* first, we'll open each of the tempfiles for input                            */
  1077.  
  1078.     for (gc = 0; gc<topfile; gc++)
  1079.         {
  1080.         tempfiles[gc] = fopen(tfilenames[gc], tempfileinmode);
  1081.         if (tempfiles[gc] == NULL)
  1082.             {
  1083.             fprintf(stderr, tempfileerror, tfilenames[gc]);
  1084.             exit(BADREAD);
  1085.             }
  1086.         readrecord(tempbuff[gc], tempfiles[gc]);
  1087.         }
  1088.  
  1089.     /* Now, we just loop through the highest lines of each file and
  1090.             see which is the lowest.  When a file runs out, we make it NULL
  1091.             so that we know not to check it's top line anymore.  When we
  1092.             find the "uppermost" line, we write it out and read another
  1093.             line to fill that file.                                                    */
  1094.  
  1095.     printf("\nMerging...");
  1096.     mergestart = clock();
  1097.  
  1098.     if (topline == 0)
  1099.         topline = -1;        /* keep from writing nothing    */
  1100.  
  1101.     while(1)
  1102.         {
  1103.  
  1104.         /* we'll initialize the biggest here to null, and then try to find
  1105.             a valid, real place for it to point to until we can find the
  1106.             biggest    */
  1107.  
  1108.             biggest = NULL;
  1109.  
  1110.             for (gc = 0; gc<topfile; gc++)
  1111.                 if (tempfiles[gc] != NULL)
  1112.                     {
  1113.                     biggest = tempbuff[gc];
  1114.                     break;
  1115.                     }
  1116.  
  1117.         /* if there are no valid datum, we'll break out of the loop    */
  1118.  
  1119.             if (biggest == NULL)
  1120.                 break;
  1121.  
  1122.         /* otherwise, we'll look through the valid data again and see if
  1123.             we can find a tempbuff bigger than the arbitrary biggest that
  1124.             we picked.        */
  1125.  
  1126.             for (gc = 0; gc<topfile; gc++)
  1127.                 if (tempfiles[gc] != NULL)
  1128.                     if (comp_gen(tempbuff[gc], biggest) <= 0)
  1129.                         {
  1130.                         biggest = tempbuff[gc];
  1131.                         bigmark = gc;
  1132.                         }
  1133.  
  1134.         /* now that we know which is the "highest" record in our buffer
  1135.             pool, we'll see if any record in memory is bigger than the
  1136.             one we think we should write.  if we find one, we will write
  1137.             it out to the file.    */
  1138.  
  1139.             if (topline != -1)
  1140.                 while(comp_gen(lines[workline], biggest) < 0)
  1141.                     {
  1142.                     writerecord(outfile, outfilename, lines[workline], workline);
  1143.                     workline++;
  1144.                     if (workline == topline)
  1145.                         {
  1146.                         topline = -1;
  1147.                         break;
  1148.                         }
  1149.                     }
  1150.  
  1151. /*    Version 1.11 fix    */
  1152.  
  1153.         /* here, we will check to see if the file we just wrote a
  1154.             record from is empty.  if it is, we will make the buffer
  1155.             NULL so we won't consider it later.    */
  1156.  
  1157.             if (tempfiles[bigmark] == NULL)
  1158.                 printf("bigmark %d was null.\n", bigmark);
  1159.  
  1160.             if (feof(tempfiles[bigmark]))
  1161.                 {
  1162.                 fclose(tempfiles[bigmark]);
  1163.                 tempfiles[bigmark] = NULL;
  1164.                 remove(tfilenames[bigmark]);
  1165.  
  1166.                 free(tempbuff[bigmark]);
  1167.                 tempbuff[bigmark] = NULL;
  1168.                 free(tfilenames[bigmark]);
  1169.                 tfilenames[bigmark] = NULL;
  1170.  
  1171.                 putchar('.');
  1172.                 }
  1173.  
  1174.         /* now, we'll write the biggest one from our buffer pool    */
  1175.         /* since that file isn't done, we will refill its entry in
  1176.             the buffer pool    */
  1177.             
  1178.             else
  1179.                 {
  1180.                 writerecord(outfile, outfilename, biggest, 0);
  1181.                 readrecord(tempbuff[bigmark], tempfiles[bigmark]);
  1182.                 }
  1183.  
  1184. /* end Version 1.11 fix    */
  1185.  
  1186.         }
  1187.  
  1188.     /* if the memory isn't empty, we will dump that to the output
  1189.         file at this time, too.    */
  1190.  
  1191.     if (topline != -1)
  1192.         while (workline < topline)
  1193.             {
  1194.             writerecord(outfile, outfilename, lines[workline], workline);
  1195.             workline++;
  1196.             }
  1197.  
  1198.     /* stop the clock!    */
  1199.  
  1200.     merging = clock() - mergestart;
  1201.  
  1202.     printf(" Merged!\n");
  1203.  
  1204.     /* close up the output file, beep at the user, and print the
  1205.         statistics    */
  1206.  
  1207.     fclose(outfile);
  1208.     puts("Sorted!\007");
  1209.     outstats();
  1210.     exit(0);
  1211. }
  1212.  
  1213.